package cn.bugstack.domain.strategy.service.armory;


import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.types.enums.ResponseCode;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

/**
 * 策略装配库实现类，负责初始化策略计算
 */
@Service
@Slf4j
public class StrategyArmoryDispatch implements IStrategyArmory , IStrategyDispatch{

    @Resource
    private IStrategyRepository repository;

    /**
     * 根据 StrategyId 完成 装配抽奖策略
     */
    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {

        //1.根据策略id查询策略奖品表，得到策略配置
        List<StrategyAwardEntity> strategyAwardEntities = repository.queryStrategyAwardList(strategyId);
        assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);

        //2.权重规则 配置 —— 符合 rule_weight
        StrategyEntity strategyEntity = repository.queryStrategyEntityByStrategyId(strategyId);
        String ruleWeight = strategyEntity.getRuleWeight();
        if(ruleWeight == null){
            return true;
        }

        //3.根据策略id 和 规则权重 查询 策略规则实体类
        StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight);
        if(strategyRuleEntity == null){
            throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
        }

        //4.在策略规则实体类中获取对应的 awardId 集合，与strategyAwardEntities的一个副本strategyAwardEntitiesClone作比较
        //删除strategyAwardEntitiesClone 中存在 但是 RuleWeightValues 的 List<Integer> 中不存在的 awardId
        Map<String, List<Integer>> ruleWeightValuesMap = strategyRuleEntity.getRuleWeightValues();
        Set<String> keys = ruleWeightValuesMap.keySet();
        for(String key : keys){
            List<Integer> awardIds = ruleWeightValuesMap.get(key);
            ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
            // 不包含的 awardId 就删掉
            strategyAwardEntitiesClone.removeIf(entity -> !awardIds.contains(entity.getAwardId()));
            assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntitiesClone);
        }

        return true;

    }



    private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities){


        //1.获取奖品策略中 最小的 概率值
        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        //2.获得概率值总和
        BigDecimal totalAwardRate = strategyAwardEntities.stream().map(StrategyAwardEntity::getAwardRate).reduce(BigDecimal.ZERO, BigDecimal::add);

        //3.计算概率对应的最大索引范围  索引范围 = totalAwardRate / minAwardRate
        BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);//代表向上取整

        //4.将每个 概率值 对应 的索引范围求出, 循环填充对应的奖品id
        List<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
        for(StrategyAwardEntity strategyAwardEntity : strategyAwardEntities){
            Integer awardId = strategyAwardEntity.getAwardId();
            BigDecimal awardRate = strategyAwardEntity.getAwardRate();
            for(int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++){
                strategyAwardSearchRateTables.add(awardId);
            }
        }

        //5.打乱 整个 策略奖品搜寻概率表
        Collections.shuffle(strategyAwardSearchRateTables);

        //6.将 打乱过后 的策略奖品搜寻概率表 集合转成 HashMap结构
        HashMap<Integer, Integer> shuffleStrategyAwardSearchRateTables = new HashMap<>();
        for(int i = 0; i < strategyAwardSearchRateTables.size(); i++){
            shuffleStrategyAwardSearchRateTables.put(i, strategyAwardSearchRateTables.get(i));
        }

        //7.存储到 redis 中
        repository.storeStrategyAwardSearchRateTables(key, shuffleStrategyAwardSearchRateTables.size(), shuffleStrategyAwardSearchRateTables);

    }


    /**
     * 根据 StrategyId 进行 抽奖活动
     */
    @Override
    public Integer getRandomAwardId(Long strategyId) {

        //1.调用仓储层接口 根据 策略id 查询(缓存查) 概率索引范围值rateRange
        int rateRange = repository.getRateRange(strategyId);

        //2.随机生成一个在范围内的数字用于 抽奖数字，通过策略id 和 这个随机生成的数字 调用仓储层接口 查询(缓存查) 概率表
        int randomNum = new SecureRandom().nextInt(rateRange);
        return repository.getStrategyAwardAssemble(String.valueOf(strategyId), randomNum);
    }

    /**
     * 根据 规则权重key 进行 抽奖活动
     * @param strategyId
     * @param ruleWeightValues
     * @return
     */
    @Override
    public Integer getRandomAwardId(Long strategyId, String ruleWeightValues) {

        String key = String.valueOf(strategyId).concat("_").concat(ruleWeightValues);

        //1.调用仓储层接口 根据 key 查询(缓存查) 概率索引范围值rateRange
        int rateRange = repository.getRateRange(key);

        //2.随机生成一个在范围内的数字用于 抽奖数字，通过 规则权重key 和 这个随机生成的数字 调用仓储层接口 查询(缓存查) 概率表
        int randomNum = new SecureRandom().nextInt(rateRange);
        return repository.getStrategyAwardAssemble(key, randomNum);
    }


}
